package com.yitiao.service;

import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yitiao.constant.AuthorityConstant;
import com.yitiao.constant.CommonConstant;
import com.yitiao.entity.CommerceUser;
import com.yitiao.mapper.CommerceUserMapper;
import com.yitiao.vo.LoginUserInfo;
import com.yitiao.vo.UsernameAndPassword;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import sun.misc.BASE64Decoder;

import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.UUID;

@Service
@AllArgsConstructor
@Slf4j
@Transactional(rollbackFor = Exception.class)
public class JWTService {
    private final CommerceUserMapper userMapper;

    /**
     * 使用默认的超时时间
     */
    public String generateToken(String username, String password) throws Exception {
        return generateToken(username, password, 0);
    }

    /**
     * 指定超时时间
     */
    public String generateToken(String username, String password, int expire) throws Exception {
        CommerceUser user = userMapper.selectOne(Wrappers.lambdaQuery(CommerceUser.class)
                .eq(CommerceUser::getUsername, username)
                .eq(CommerceUser::getPassword, password)
        );
        if (ObjectUtil.isEmpty(user)) {
            log.info("can not find user: [{}],[{}].", username, password);
            return null;
        }
        // 记录用户信息 需要用 ThreadLocal
        LoginUserInfo userInfo = LoginUserInfo.builder()
                .id(user.getId())
                .username(user.getUsername())
                .build();

        if (expire <= 0) {
            expire = AuthorityConstant.DEFAULT_EXPIRE_DAY;
        }

        // 计算超时时间  当前时间 + 过期天数
        ZonedDateTime time = LocalDate.now().plus(expire, ChronoUnit.DAYS).atStartOfDay(ZoneId.systemDefault());
        Date expireDate = Date.from(time.toInstant());

        return Jwts.builder()
                // jwt payload --> KV
                .claim(CommonConstant.JWT_USER_INFO_KEY, JSON.toJSONString(userInfo))
                // jwt id
                .setId(UUID.randomUUID().toString())
                // jwt 过期时间
                .setExpiration(expireDate)
                // jwt 签名 --> 加密
                .signWith(getPrivateKey(), SignatureAlgorithm.RS256)
                .compact();
    }

    public String registerUserAndGenerateToken(UsernameAndPassword usernameAndPassword) throws Exception {
        CommerceUser oldUser = userMapper.selectOne(Wrappers.lambdaQuery(CommerceUser.class).eq(CommerceUser::getUsername, usernameAndPassword.getUsername()));
        if (ObjectUtil.isNotEmpty(oldUser)){
            log.error("username is registered: [{}]", oldUser.getUsername());
            return null;
        }

        CommerceUser newUser = CommerceUser.builder()
//                .id(IdUtil.getSnowflake().nextId())
                .username(usernameAndPassword.getUsername())
                .password(usernameAndPassword.getPassword())
                .extraInfo("{}")
                .createTime(new Date())
                .updateTime(new Date())
                .build();

        userMapper.insert(newUser);
        log.info("register user success: [{}], [{}]", newUser.getUsername(), newUser.getId());

        return generateToken(newUser.getUsername(),newUser.getPassword());
    }

    /**
     * <h2>根据本地存储的私钥获取到 PrivateKey 对象</h2>
     * */
    private PrivateKey getPrivateKey() throws Exception {

        PKCS8EncodedKeySpec priPKCS8 = new PKCS8EncodedKeySpec(
                new BASE64Decoder().decodeBuffer(AuthorityConstant.PRIVATE_KEY));
        KeyFactory keyFactory = KeyFactory.getInstance("RSA");
        return keyFactory.generatePrivate(priPKCS8);
    }
}
